package negocios;

import java.util.ArrayList;
import java.util.List;

import negocios.excecoes.EntradaInvalidaException;
import negocios.excecoes.LocalizacaoException;
import negocios.excecoes.NaoEncontradoException;
import util.Criptografia;

/**
 * Classe abstrata que implementa a interface Usuario.
 * 
 * @author Jonathan Brilhante
 * @author Jose Rafael
 * @author Nata Venancio
 * @author Renato Almeida
 * 
 * @version 2.0
 */

public abstract class UsuarioAbstrato implements Usuario, Comparable<Usuario> {
	private static final int LIMITE_INF_SENHA = 6;
	private static final int MINIMO_TAM_TEL = 8;
	private static final int MAXIMO_TAM_TEL = 12;

	protected String nome;
	protected String senha;
	protected String telefone;
	protected String email;
	protected String login;
	protected Localizador localizador;
	protected List<Relacionamento> amigos;
	protected CaixaPostal caixaPostal;
	protected boolean logado;

	/**
	 * Constroi um Usuario com seus dados obrigatorios
	 * 
	 * @param login
	 *            o login do usuario
	 * @param nome
	 *            o nome do usuario
	 * @param email
	 *            o email do usuario
	 * @param senha
	 *            a senha do usuario
	 * @param telefone
	 *            o telefone do usuario
	 * @throws EntradaInvalidaException
	 *             caso algum dado seja invalido
	 */
	public UsuarioAbstrato(String login, String nome, String email,
			String senha, String telefone) throws EntradaInvalidaException {
		if (login == null || login.equals("")) {
			throw new EntradaInvalidaException(
					"Username eh um dado obrigatorio.");
		} else if (senha == null) {
			throw new EntradaInvalidaException("Senha eh um dado obrigatorio.");
		} else if (senha.length() < LIMITE_INF_SENHA) {
			throw new EntradaInvalidaException(
					"Senha deve ter no minimo 6 caracteres.");
		}
		try {
			this.localizador = new LocalizadorIP("127.0.0.1");
		} catch (LocalizacaoException e) {
			// nunca acontecera :)
		}
		this.senha = senha;
		this.login = login;
		setNome(nome);
		setEmail(email);
		setTelefone(telefone);
		deslogar();
		amigos = new ArrayList<Relacionamento>();
		caixaPostal = new CaixaPostal();
	}

	public List<Relacionamento> amigos() {
		return amigos;
	}

	public void logar() {
		logado = true;
	}

	public void deslogar() {
		logado = false;
	}

	public void excluirAmigo(Usuario amigo) throws NaoEncontradoException {
		Relacionamento aRemover = null;
		for (Relacionamento a : amigos) {
			if (a.getAmigo().equals(amigo)) {
				aRemover = a;
				break;
			}
		}
		if (aRemover == null) {
			throw new NaoEncontradoException("Usuario desconhecido.");
		}
		this.amigos.remove(aRemover);
	}

	public String getEmail() {
		return this.email;
	}

	public String getLogin() {
		return this.login;
	}

	public String getNome() {
		return this.nome;
	}

	public String getSenha() {
		return this.senha;
	}

	public String getTelefone() {
		return this.telefone;
	}

	public CaixaPostal getCaixaPostal() {
		return this.caixaPostal;
	}

	public boolean isLogado() {
		return this.logado;
	}

	public Localizador getLocalizador() {
		return this.localizador;
	}

	public void setLocalizador(Localizador loc) {
		this.localizador = loc;
	}

	public void setEmail(String email) throws EntradaInvalidaException {
		if (email == null || email.equals(""))
			throw new EntradaInvalidaException("E-mail eh um dado obrigatorio.");
		if (!email.contains("@"))
			throw new EntradaInvalidaException("E-mail invalido.");
		this.email = email;
	}

	public void setNome(String nome) throws EntradaInvalidaException {
		if (nome == null || nome.equals(""))
			throw new EntradaInvalidaException("Nome eh um dado obrigatorio.");
		this.nome = nome;
	}

	public void setSenha(String novaSenha) throws EntradaInvalidaException {
		if (novaSenha == null || novaSenha.length() < LIMITE_INF_SENHA) {
			throw new EntradaInvalidaException(
					"Senha deve ter no minimo 6 caracteres.");
		}
		this.senha = Criptografia.cifra(novaSenha);
	}

	public void setTelefone(String telefone) throws EntradaInvalidaException {
		if (telefone == null || telefone.length() < MINIMO_TAM_TEL
				|| telefone.length() > MAXIMO_TAM_TEL)
			throw new EntradaInvalidaException("Entrada de telefone invalida");
		for (int i = 0; i < telefone.length(); i++) {
			char c = telefone.charAt(i);
			if (!Character.isDigit(c)) {
				throw new EntradaInvalidaException(
						"Entrada de telefone invalida");
			}
		}
		this.telefone = telefone;
	}

	public void adicionaAmigo(Usuario amigo) throws EntradaInvalidaException {
		adicionaAmigo(amigo, false);
	}

	public void adicionaAmigo(Usuario amigo, boolean oculto)
			throws EntradaInvalidaException {
		if (amigo.equals(this)) {
			throw new EntradaInvalidaException(
					"Voce nao pode ser amigo de voce mesmo");
		}
		for (Relacionamento a : amigos) {
			if (a.getAmigo().equals(amigo)) {
				throw new EntradaInvalidaException("Esse usuario ja eh amigo");
			}
		}
		amigos.add(new Relacionamento(amigo, getLogin(), oculto));
	}

	public Localizacao localizacao() throws LocalizacaoException {
		return this.localizador.getLocalidade();
	}

	/**
	 * Retorna um inteiro resultado da comparacao entre os usuarios. A
	 * comparacao eh feita pela ordem lexicografica dos nomes
	 * 
	 * @param outro
	 *            o outro usuario
	 * @return 0 se forem iguais, 1 se o usuario for maior que "outro" na ordem,
	 *         e -1 se for menor
	 */
	public int compareTo(Usuario outro) {
		return getNome().compareTo(outro.getNome());
	}

	/**
	 * Responde se os objetos sao iguais
	 * 
	 * @param outro
	 *            o outro objeto a comparar
	 * @return true se forem iguais (mesmo login), false caso contrario
	 */
	public boolean equals(Object outro) {
		if (!(outro instanceof UsuarioAbstrato)) {
			return false;
		}
		UsuarioAbstrato o = (UsuarioAbstrato) outro;
		return o.login.equals(login);
	}

	/**
	 * Retorna uma string representando o usuario, no formato nome (email)
	 * 
	 * @return a string representando o usuario
	 */
	public String toString() {
		return getNome() + "(" + getEmail() + ")";
	}
}